<!DOCTYPE html>
<html>
<!--
Copyright 2007 The Closure Library Authors. All Rights Reserved.

Use of this source code is governed by the Apache License, Version 2.0.
See the COPYING file for details.
-->
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Closure Unit Tests - goog.testing.asserts</title>
<script src="../base.js"></script>
<script>
  goog.require('goog.dom');
  goog.require('goog.iter.Iterator');
  goog.require('goog.iter.StopIteration');
  goog.require('goog.structs.Map');
  goog.require('goog.structs.Set');
  goog.require('goog.testing.asserts');
  goog.require('goog.testing.jsunit');
  goog.require('goog.userAgent');
</script>
</head>
<body>
<script>

  function testAssertTrue() {
    assertTrue(true);
    assertTrue('Good assertion', true);
    assertThrowsJsUnitException(
        function() { assertTrue(false); },
        'Call to assertTrue(boolean) with false');
    assertThrowsJsUnitException(
        function() { assertTrue('Should be true', false); },
        'Should be true\nCall to assertTrue(boolean) with false');
    assertThrowsJsUnitException(
        function() { assertTrue(null); },
        'Bad argument to assertTrue(boolean)');
    assertThrowsJsUnitException(
        function() { assertTrue(undefined); },
        'Bad argument to assertTrue(boolean)');
  }

  function testAssertFalse() {
    assertFalse(false);
    assertFalse('Good assertion', false);
    assertThrowsJsUnitException(
        function() { assertFalse(true); },
        'Call to assertFalse(boolean) with true');
    assertThrowsJsUnitException(
        function() { assertFalse('Should be false', true); },
        'Should be false\nCall to assertFalse(boolean) with true');
    assertThrowsJsUnitException(
        function() { assertFalse(null); },
        'Bad argument to assertFalse(boolean)');
    assertThrowsJsUnitException(
        function() { assertFalse(undefined); },
        'Bad argument to assertFalse(boolean)');
  }

  function testAssertEqualsWithString() {
    assertEquals('a', 'a');
    assertEquals('Good assertion', 'a', 'a');
    assertThrowsJsUnitException(
        function() { assertEquals('a', 'b'); },
        'Expected <a> (String) but was <b> (String)');
    assertThrowsJsUnitException(
        function() { assertEquals('Bad assertion', 'a', 'b'); },
        'Bad assertion\nExpected <a> (String) but was <b> (String)');
  }

  function testAssertEqualsWithInteger() {
    assertEquals(1, 1);
    assertEquals('Good assertion', 1, 1);
    assertThrowsJsUnitException(
        function() { assertEquals(1, 2); },
        'Expected <1> (Number) but was <2> (Number)');
    assertThrowsJsUnitException(
        function() { assertEquals('Bad assertion', 1, 2); },
        'Bad assertion\nExpected <1> (Number) but was <2> (Number)');
  }

  function testAssertNotEquals() {
    assertNotEquals('a', 'b');
    assertNotEquals('a', 'a', 'b');
    assertThrowsJsUnitException(
        function() { assertNotEquals('a', 'a'); },
        'Expected not to be <a> (String)');
    assertThrowsJsUnitException(
        function() { assertNotEquals('a', 'a', 'a'); },
        'a\nExpected not to be <a> (String)');
  }

  function testAssertNull() {
    assertNull(null);
    assertNull('Good assertion', null);
    assertThrowsJsUnitException(
        function() { assertNull(true); },
        'Expected <null> but was <true> (Boolean)');
    assertThrowsJsUnitException(
        function() { assertNull('Should be null', false); },
        'Should be null\nExpected <null> but was <false> (Boolean)');
    assertThrowsJsUnitException(
        function() { assertNull(undefined); },
        'Expected <null> but was <undefined>');
    assertThrowsJsUnitException(
        function() { assertNull(1); },
        'Expected <null> but was <1> (Number)');
  }

  function testAssertNotNull() {
    assertNotNull(true);
    assertNotNull('Good assertion', true);
    assertNotNull(false);
    assertNotNull(undefined);
    assertNotNull(1);
    assertNotNull('a');
    assertThrowsJsUnitException(
        function() { assertNotNull(null); },
        'Expected not to be <null>');
    assertThrowsJsUnitException(
        function() { assertNotNull('Should not be null', null); },
        'Should not be null\nExpected not to be <null>');
  }

  function testAssertUndefined() {
    assertUndefined(undefined);
    assertUndefined('Good assertion', undefined);
    assertThrowsJsUnitException(
        function() { assertUndefined(true); },
        'Expected <undefined> but was <true> (Boolean)');
    assertThrowsJsUnitException(
        function() { assertUndefined('Should be undefined', false); },
        'Should be undefined\nExpected <undefined> but was <false> (Boolean)');
    assertThrowsJsUnitException(
        function() { assertUndefined(null); },
        'Expected <undefined> but was <null>');
    assertThrowsJsUnitException(
        function() { assertUndefined(1); },
        'Expected <undefined> but was <1> (Number)');
  }

  function testAssertNotUndefined() {
    assertNotUndefined(true);
    assertNotUndefined('Good assertion', true);
    assertNotUndefined(false);
    assertNotUndefined(null);
    assertNotUndefined(1);
    assertNotUndefined('a');
    assertThrowsJsUnitException(
        function() { assertNotUndefined(undefined); },
                  'Expected not to be <undefined>');
    assertThrowsJsUnitException(
        function() { assertNotUndefined('Should not be undefined', undefined); },
        'Should not be undefined\nExpected not to be <undefined>');
  }

  function testAssertNotNullNorUndefined() {
    assertNotNullNorUndefined(true);
    assertNotNullNorUndefined('Good assertion', true);
    assertNotNullNorUndefined(false);
    assertNotNullNorUndefined(1);
    assertNotNullNorUndefined(0);
    assertNotNullNorUndefined('a');
    assertThrowsJsUnitException(function() {
      assertNotNullNorUndefined(undefined);
    }, 'Expected not to be <undefined>');
    assertThrowsJsUnitException(function() {
      assertNotNullNorUndefined('Should not be undefined', undefined);
    }, 'Should not be undefined\nExpected not to be <undefined>');
    assertThrowsJsUnitException(function() {
      assertNotNullNorUndefined(null);
    }, 'Expected not to be <null>');
    assertThrowsJsUnitException(function() {
      assertNotNullNorUndefined('Should not be null', null);
    }, 'Should not be null\nExpected not to be <null>');
  }

  function testAssertNonEmptyString() {
    assertNonEmptyString('hello');
    assertNonEmptyString('Good assertion', 'hello');
    assertNonEmptyString('true');
    assertNonEmptyString('false');
    assertNonEmptyString('1');
    assertNonEmptyString('null');
    assertNonEmptyString('undefined');
    assertNonEmptyString('\n');
    assertNonEmptyString(' ');
    assertThrowsJsUnitException(
        function() { assertNonEmptyString(''); },
        'Expected non-empty string but was <> (String)');
    assertThrowsJsUnitException(
        function() { assertNonEmptyString('Should be non-empty string', ''); },
        'Should be non-empty string\n' +
          'Expected non-empty string but was <> (String)');
    assertThrowsJsUnitException(
        function() { assertNonEmptyString(true); },
        'Expected non-empty string but was <true> (Boolean)');
    assertThrowsJsUnitException(
        function() { assertNonEmptyString(false); },
        'Expected non-empty string but was <false> (Boolean)');
    assertThrowsJsUnitException(
        function() { assertNonEmptyString(1); },
        'Expected non-empty string but was <1> (Number)');
    assertThrowsJsUnitException(
        function() { assertNonEmptyString(null); },
        'Expected non-empty string but was <null>');
    assertThrowsJsUnitException(
        function() { assertNonEmptyString(undefined); },
        'Expected non-empty string but was <undefined>');
    assertThrowsJsUnitException(
        function() { assertNonEmptyString(['hello']); },
        'Expected non-empty string but was <hello> (Array)');
    // Different browsers return different values/types in the failure message
    // so don't bother checking if the message is exactly as expected.
    assertThrowsJsUnitException(
        function() { assertNonEmptyString(goog.dom.createTextNode('hello')); });
  }

  function testAssertNaN() {
    assertNaN(NaN);
    assertNaN('Good assertion', NaN);
    assertThrowsJsUnitException(
        function() { assertNaN(1); }, 'Expected NaN');
    assertThrowsJsUnitException(
        function() { assertNaN('Should be NaN', 1); },
        'Should be NaN\nExpected NaN');
    assertThrowsJsUnitException(
        function() { assertNaN(true); }, 'Expected NaN');
    assertThrowsJsUnitException(
        function() { assertNaN(false); }, 'Expected NaN');
    assertThrowsJsUnitException(
        function() { assertNaN(null); }, 'Expected NaN');
    assertThrowsJsUnitException(
        function() { assertNaN(''); }, 'Expected NaN');

    // TODO(user): These assertions fail. We should decide on the
    // semantics of assertNaN
    //assertThrowsJsUnitException(function() { assertNaN(undefined); },
    //    'Expected NaN');
    //assertThrowsJsUnitException(function() { assertNaN('a'); },
    //    'Expected NaN');
  }

  function testAssertNotNaN() {
    assertNotNaN(1);
    assertNotNaN('Good assertion', 1);
    assertNotNaN(true);
    assertNotNaN(false);
    assertNotNaN('');
    assertNotNaN(null);

    // TODO(user): These assertions fail. We should decide on the
    // semantics of assertNotNaN
    //assertNotNaN(undefined);
    //assertNotNaN('a');

    assertThrowsJsUnitException(
        function() { assertNotNaN(Number.NaN); },
        'Expected not NaN');
    assertThrowsJsUnitException(
        function() { assertNotNaN('Should not be NaN', Number.NaN); },
        'Should not be NaN\nExpected not NaN');
  }

  function testAssertObjectEquals() {
    var obj1 = [{'a': 'hello', 'b': 'world'}];
    var obj2 = [{'a': 'hello', 'c': 'dear', 'b' : 'world'}];

    // Check with obj1 and obj2 as first and second arguments respectively.
    assertThrows('Objects should not be equal', function() {
      assertObjectEquals(obj1, obj2);
    });

    // Check with obj1 and obj2 as second and first arguments respectively.
    assertThrows('Objects should not be equal', function() {
      assertObjectEquals(obj2, obj1);
    });

    // Test if equal objects are considered equal.
    var obj3 = [{'b': 'world', 'a': 'hello'}];
    assertObjectEquals(obj1, obj3);
    assertObjectEquals(obj3, obj1);

    // Test with a case where one of the members has an undefined value.
    var obj4 = [{'a': 'hello', 'b': undefined}];
    var obj5 = [{'a': 'hello'}];

    // Check with obj4 and obj5 as first and second arguments respectively.
    assertThrows('Objects should not be equal', function() {
      assertObjectEquals(obj4, obj5);
    });

    // Check with obj5 and obj4 as first and second arguments respectively.
    assertThrows('Objects should not be equal', function() {
      assertObjectEquals(obj5, obj4);
    });
  }

  function testAssertObjectNotEquals() {
    var obj1 = [{'a': 'hello', 'b': 'world'}];
    var obj2 = [{'a': 'hello', 'c': 'dear', 'b' : 'world'}];

    // Check with obj1 and obj2 as first and second arguments respectively.
    assertObjectNotEquals(obj1, obj2);

    // Check with obj1 and obj2 as second and first arguments respectively.
    assertObjectNotEquals(obj2, obj1);

    // Test if equal objects are considered equal.
    var obj3 = [{'b': 'world', 'a': 'hello'}];
    var error = assertThrows('Objects should be equal', function() {
      assertObjectNotEquals(obj1, obj3);
    });
    assertContains('Objects should not be equal', error.message);
    error = assertThrows('Objects should be equal', function() {
      assertObjectNotEquals(obj3, obj1);
    });
    assertContains('Objects should not be equal', error.message);

    // Test with a case where one of the members has an undefined value.
    var obj4 = [{'a': 'hello', 'b': undefined}];
    var obj5 = [{'a': 'hello'}];

    // Check with obj4 and obj5 as first and second arguments respectively.
    assertObjectNotEquals(obj4, obj5);

    // Check with obj5 and obj4 as first and second arguments respectively.
    assertObjectNotEquals(obj5, obj4);
  }

  function testAssertObjectEquals2() {
    // NOTE: (0 in [undefined]) is true on FF but false on IE.
    // (0 in {'0': undefined}) is true on both.
    // grrr.
    assertObjectEquals('arrays should be equal', [undefined], [undefined]);
    assertThrows('arrays should not be equal', function() {
      assertObjectEquals([undefined, undefined], [undefined]);
    });
    assertThrows('arrays should not be equal', function() {
      assertObjectEquals([undefined], [undefined, undefined]);
    });
  }

  function testAssertObjectEquals3() {
    // Check that objects that contain identical Map objects compare
    // as equals. We can't do a negative test because on browsers that
    // implement __iterator__ we can't check the values of the iterated
    // properties.
    var obj1 = [{'a': 'hi',
                 'b': new goog.structs.Map('hola', 'amigo',
                                           'como', 'estas?')},
                14,
                'yes',
                true];
    var obj2 = [{'a': 'hi',
                 'b': new goog.structs.Map('hola', 'amigo',
                                           'como', 'estas?')},
                14,
                'yes',
                true];
    assertObjectEquals('Objects should be equal', obj1, obj2);

    var obj3 = {'a': [1, 2]};
    var obj4 = {'a': [1, 2, 3]};
    assertThrows('inner arrays should not be equal', function() {
      assertObjectEquals(obj3, obj4);
    });
    assertThrows('inner arrays should not be equal', function() {
      assertObjectEquals(obj4, obj3);
    });
  }

  function testAssertObjectEqualsSet() {
    // verify that Sets compare equal, when run in an environment that
    // supports iterators
    var set1 = new goog.structs.Set();
    var set2 = new goog.structs.Set();

    set1.add('a');
    set1.add('b');
    set1.add(13);

    set2.add('a');
    set2.add('b');
    set2.add(13);

    assertObjectEquals('sets should be equal', set1, set2);

    set2.add('hey');
    assertThrows('sets should not be equal', function() {
      assertObjectEquals(set1, set2);
    });
  }

  function testAssertObjectEqualsIterNoEquals() {
    // an object with an iterator but no equals() and no map_ cannot
    // be compared
    function Thing() {
      this.what = [];
    }
    Thing.prototype.add = function(n, v) {
      this.what.push(n + '@' + v);
    };
    Thing.prototype.get = function(n) {
      var m = new RegExp('^' + n + '@(.*)$', '');
      for (var i = 0; i < this.what.length; ++i) {
        var match = this.what[i].match(m);
        if (match) {
          return match[1];
        }
      }
      return null;
    };
    Thing.prototype.__iterator__ = function() {
      var iter = new goog.iter.Iterator;
      iter.index = 0;
      iter.thing = this;
      iter.next = function() {
        if (this.index < this.thing.what.length) {
          return this.thing.what[this.index++].split('@')[0];
        } else {
          throw goog.iter.StopIteration;
        }
      };
      return iter;
    };

    var thing1 = new Thing(); thing1.name = 'thing1';
    var thing2 = new Thing(); thing2.name = 'thing2';
    thing1.add('red', 'fish');
    thing1.add('blue', 'fish');

    thing2.add('red', 'fish');
    thing2.add('blue', 'fish');

    assertThrows('things should not be equal', function() {
      assertObjectEquals(thing1, thing2);
    });
  }

  function testAssertObjectEqualsWithDates() {
    var date = new Date(2010, 0, 1);
    var dateWithMilliseconds = new Date(2010, 0, 1, 0, 0, 0, 1);
    assertObjectEquals(new Date(2010, 0, 1), date);
    assertThrows(goog.partial(assertObjectEquals, date, dateWithMilliseconds));
  }

  function testAssertObjectEqualsSparseArrays() {
    var arr1 = [,2,,4,,];
    var arr2 = [1,2,3,4,5];

    assertThrows('Sparse arrays should not be equal', function() {
      assertObjectEquals(arr1, arr2);
    });

    assertThrows('Sparse arrays should not be equal', function() {
      assertObjectEquals(arr2, arr1);
    });
  }

  function testAssertObjectEqualsSparseArrays2() {
    // On IE6-8, the expression "1 in [4,undefined]" evaluates to false,
    // but true on other browsers. FML. This test verifies a regression
    // where IE reported that arr4 was not equal to arr1 or arr2.
    var arr1 = [1,,3];
    var arr2 = [1,undefined,3];
    var arr3 = goog.array.clone(arr1);
    var arr4 = [];
    arr4.push(1, undefined, 3);

    // Assert that all those arrays are equivalent pairwise.
    var arrays = [arr1, arr2, arr3, arr4];
    for (var i = 0; i < arrays.length; i++) {
      for (var j = 0; j < arrays.length; j++) {
        assertArrayEquals(arrays[i], arrays[j]);
      }
    }
  }

  function testAssertObjectEqualsArraysWithExtraProps() {
    var arr1 = [1];
    var arr2 = [1];
    arr2.foo = 3;

    assertThrows('Aarrays should not be equal', function() {
      assertObjectEquals(arr1, arr2);
    });

    assertThrows('Arrays should not be equal', function() {
      assertObjectEquals(arr2, arr1);
    });
  }

  function testAssertSameElementsOnArray() {
    assertSameElements([1, 2], [2, 1]);
    assertSameElements('Good assertion', [1, 2], [2, 1]);
    assertSameElements('Good assertion with duplicates', [1, 1, 2], [2, 1, 1]);
    assertThrowsJsUnitException(
        function() {
          assertSameElements([1, 2], [1]);
        },
        'Expected 2 elements: [1,2], got 1 elements: [1]');
    assertThrowsJsUnitException(
        function() {
          assertSameElements('Should match', [1, 2], [1]);
        },
        'Should match\nExpected 2 elements: [1,2], got 1 elements: [1]');
    assertThrowsJsUnitException(
        function() {
          assertSameElements([1, 2], [1, 3]);
        },
        'Expected [1,2], got [1,3]');
    assertThrowsJsUnitException(
        function() {
          assertSameElements('Should match', [1, 2], [1, 3]);
        },
        'Should match\nExpected [1,2], got [1,3]');
    assertThrowsJsUnitException(
        function() {
          assertSameElements([1, 1, 2], [2, 2, 1]);
        },
        'Expected [1,1,2], got [2,2,1]');
  }

  function testAssertSameElementsOnArrayLike() {
    assertSameElements({0: 0, 1: 1, length: 2}, {length: 2, 1: 1, 0: 0});
    assertThrowsJsUnitException(
        function() {
          assertSameElements({0: 0, 1: 1, length: 2}, {0: 0, length: 1});
        },
        'Expected 2 elements: [0,1], got 1 elements: [0]');
  }

  function testAssertSameElementsWithBadArguments() {
    assertThrowsJsUnitException(
        function() {
          assertSameElements([], new goog.structs.Set());
        },
        'Bad arguments to assertSameElements(opt_message, expected: ' +
        'ArrayLike, actual: ArrayLike)\n' +
        'Call to assertTrue(boolean) with false');
  }

  var implicitlyTrue = [
    true,
    1,
    -1,
    ' ',
    'string',
    Infinity,
    new Object()
  ];

  var implicitlyFalse = [
    false,
    0,
    '',
    null,
    undefined,
    NaN
  ];

  function testAssertEvaluatesToTrue() {
    assertEvaluatesToTrue(true);
    assertEvaluatesToTrue('', true);
    assertEvaluatesToTrue('Good assertion', true);
    assertThrowsJsUnitException(
        function() { assertEvaluatesToTrue(false); },
        'Expected to evaluate to true');
    assertThrowsJsUnitException(
        function() {assertEvaluatesToTrue('Should be true', false); },
        'Should be true\nExpected to evaluate to true');
    for (var i = 0; i < implicitlyTrue.length; i++) {
      assertEvaluatesToTrue(String('Test ' + implicitlyTrue[i] +
          ' [' + i + ']'), implicitlyTrue[i]);
    }
    for (var i = 0; i < implicitlyFalse.length; i++) {
      assertThrowsJsUnitException(
          function() { assertEvaluatesToTrue(implicitlyFalse[i]); },
          'Expected to evaluate to true');
    }
  }

  function testAssertEvaluatesToFalse() {
    assertEvaluatesToFalse(false);
    assertEvaluatesToFalse('Good assertion', false);
    assertThrowsJsUnitException(
        function() { assertEvaluatesToFalse(true); },
        'Expected to evaluate to false');
    assertThrowsJsUnitException(
        function() { assertEvaluatesToFalse('Should be false', true); },
        'Should be false\nExpected to evaluate to false');
    for (var i = 0; i < implicitlyFalse.length; i++) {
      assertEvaluatesToFalse(String('Test ' + implicitlyFalse[i] +
          ' [' + i + ']'), implicitlyFalse[i]);
    }
    for (var i = 0; i < implicitlyTrue.length; i++) {
      assertThrowsJsUnitException(
          function() { assertEvaluatesToFalse(implicitlyTrue[i]); },
          'Expected to evaluate to false');
    }
  }

  function testAssertHTMLEquals() {
    // TODO
  }

  function testAssertHashEquals() {
    assertHashEquals({a: 1, b: 2}, {b: 2, a: 1});
    assertHashEquals('Good assertion', {a: 1, b: 2}, {b: 2, a: 1});
    assertHashEquals({a: undefined}, {a: undefined});
    // Missing key.
    assertThrowsJsUnitException(
        function() { assertHashEquals({a: 1, b: 2}, {a: 1}); },
        'Expected hash had key b that was not found');
    assertThrowsJsUnitException(
        function() { assertHashEquals('Should match', {a: 1, b: 2}, {a: 1}); },
        'Should match\nExpected hash had key b that was not found');
    assertThrowsJsUnitException(
        function() { assertHashEquals({a: undefined}, {}); },
        'Expected hash had key a that was not found');
    // Not equal key.
    assertThrowsJsUnitException(
        function() { assertHashEquals({a: 1}, {a: 5}); },
        'Value for key a mismatch - expected = 1, actual = 5');
    assertThrowsJsUnitException(
        function() { assertHashEquals('Should match', {a: 1}, {a: 5}); },
        'Should match\nValue for key a mismatch - expected = 1, actual = 5');
    assertThrowsJsUnitException(
        function() { assertHashEquals({a: undefined}, {a: 1})},
        'Value for key a mismatch - expected = undefined, actual = 1');
    // Extra key.
    assertThrowsJsUnitException(
        function() { assertHashEquals({a: 1}, {a: 1, b: 1}); },
        'Actual hash had key b that was not expected');
    assertThrowsJsUnitException(
        function() { assertHashEquals('Should match', {a: 1}, {a: 1, b: 1}); },
        'Should match\nActual hash had key b that was not expected');
  }

  function testAssertRoughlyEquals() {
    assertRoughlyEquals(1, 1, 0);
    assertRoughlyEquals('Good assertion', 1, 1, 0);
    assertRoughlyEquals(1, 1.1, 0.11);
    assertRoughlyEquals(1.1, 1, 0.11);
    assertThrowsJsUnitException(
        function() { assertRoughlyEquals(1, 1.1, 0.05); },
        'Expected 1, but got 1.1 which was more than 0.05 away');
    assertThrowsJsUnitException(
        function() { assertRoughlyEquals('Close enough', 1, 1.1, 0.05); },
        'Close enough\nExpected 1, but got 1.1 which was more than 0.05 away');
  }

  function testAssertContains() {
    var a = [1, 2, 3];
    assertContains(1, [1, 2, 3]);
    assertContains('Should contain', 1, [1, 2, 3]);
    assertThrowsJsUnitException(
        function() { assertContains(4, [1, 2, 3]); },
        'Expected \'1,2,3\' to contain \'4\'');
    assertThrowsJsUnitException(
        function() { assertContains('Should contain', 4, [1, 2, 3]); },
        'Should contain\nExpected \'1,2,3\' to contain \'4\'');
    // assertContains uses ===.
    var o = new Object();
    assertContains(o, [o, 2, 3]);
    assertThrowsJsUnitException(
        function() { assertContains(o, [1, 2, 3]); },
        'Expected \'1,2,3\' to contain \'[object Object]\'');
  }

  function testAssertNotContains() {
    var a = [1, 2, 3];
    assertNotContains(4, [1, 2, 3]);
    assertNotContains('Should not contain', 4, [1, 2, 3]);
    assertThrowsJsUnitException(
        function() { assertNotContains(1, [1, 2, 3]); },
        'Expected \'1,2,3\' not to contain \'1\'');
    assertThrowsJsUnitException(
        function() { assertNotContains('Should not contain', 1, [1, 2, 3]); },
        "Should not contain\nExpected '1,2,3' not to contain '1'");
    // assertNotContains uses ===.
    var o = new Object();
    assertNotContains({}, [o, 2, 3]);
    assertThrowsJsUnitException(
        function() { assertNotContains(o, [o, 2, 3]); },
        'Expected \'[object Object],2,3\' not to contain \'[object Object]\'');
  }

  function testAssertThrows() {
    var failed = false;
    try {
      assertThrows('assertThrows should not pass with null param', null);
      failed = true;
    } catch (e) {
    }
    assertFalse('Fail should not get set to true', failed);

    try {
      assertThrows(
          'assertThrows should not pass with undefined param',
          undefined);
      failed = true;
    } catch (e) {
    }
    assertFalse('Fail should not get set to true', failed);

    try {
      assertThrows('assertThrows should not pass with number param', 1);
      failed = true;
    } catch (e) {
    }
    assertFalse('Fail should not get set to true', failed);

    try {
      assertThrows('assertThrows should not pass with string param', 'string');
      failed = true;
    } catch (e) {
    }
    assertFalse('Fail should not get set to true', failed);

    try {
      assertThrows('assertThrows should not pass with object param', {});
      failed = true;
    } catch (e) {
    }
    assertFalse('Fail should not get set to true', failed);

    try {
      var error = assertThrows('valid function throws Error', function() {
        throw new Error('test');
      });
    } catch (e) {
      fail('assertThrows incorrectly doesn\'t detect a thrown exception');
    }
    assertEquals('error message', 'test', error.message);

    try {
      var stringError = assertThrows('valid function throws string error',
          function() {
            throw 'string error test';
          });
    } catch (e) {
      fail('assertThrows doesn\'t detect a thrown string exception');
    }
    assertEquals('string error', 'string error test', stringError);
  }

  function testAssertNotThrows() {
    var failed = false;
    try {
      assertNotThrows('assertNotThrows should not pass with null param', null);
      failed = true;
    } catch (e) {
    }
    assertFalse('Fail should not get set to true', failed);

    try {
      assertNotThrows(
          'assertNotThrows should not pass with undefined param', undefined);
      failed = true;
    } catch (e) {
    }
    assertFalse('Fail should not get set to true', failed);

    try {
      assertNotThrows('assertNotThrows should not pass with number param', 1);
      failed = true;
    } catch (e) {
    }
    assertFalse('Fail should not get set to true', failed);

    try {
      assertNotThrows('assertNotThrows should not pass with string param',
          'string');
      failed = true;
    } catch (e) {
    }
    assertFalse('Fail should not get set to true', failed);


    try {
      assertNotThrows('assertNotThrows should not pass with object param', {});
      failed = true;
    } catch (e) {
    }
    assertFalse('Fail should not get set to true', failed);


    var result;
    try {
      result = assertNotThrows('valid function',
          function() {
            return 'some value';
          });
    } catch (e) {
      // Shouldn't be here: throw exception.
      fail('assertNotThrows returned failure on a valid function');
    }
    assertEquals('assertNotThrows should return the result of the function.',
        'some value', result);

    var errorDescription = 'a test error exception';
    try {
      assertNotThrows('non valid error throwing function',
          function() {
            throw new Error(errorDescription);
          });
      failed = true;
    } catch (e) {
      // Some browsers don't have a stack trace so expect to at least have the
      // error description. For Gecko and IE7 not even that is included.
      if (!goog.userAgent.GECKO &&
          (!goog.userAgent.IE || goog.userAgent.isVersionOrHigher('8'))) {
        assertContains(errorDescription, e.message);
      }
    }
    assertFalse('assertNotThrows did not fail on a thrown exception', failed);
  }

  function testAssertArrayEquals() {
    var a1 = [0, 1, 2];
    var a2 = [0, 1, 2];
    assertArrayEquals('Arrays should be equal', a1, a2);

    assertThrows('Should have thrown because args are not arrays', function() {
      assertArrayEquals(true, true);
    });

    a1 = [0, undefined, 2];
    a2 = [0, , 2];
    // The following test fails unexpectedly. The bug is tracked at
    // http://code.google.com/p/closure-library/issues/detail?id=419
    // assertThrows(
    //     'assertArrayEquals distinguishes undefined items from sparse arrays',
    //     function() {
    //       assertArrayEquals(a1, a2);
    //     });

    // For the record. This behavior will probably change in the future.
    assertArrayEquals(
        'Bug: sparse arrays and undefined items are not distinguished',
        [0, undefined, 2], [0, , 2]);

    assertThrows('The array elements should be compared with ===', function() {
      assertArrayEquals([0], ['0'])
    });

    assertThrows('Arrays with different length should be different',
        function() {
          assertArrayEquals([0, undefined], [0])
        });

    a1 = [0];
    a2 = [0];
    a2[-1] = -1;
    assertArrayEquals('Negative indexes are ignored', a1, a2);

    a1 = [0];
    a2 = [0];
    a2['extra'] = 1
    assertArrayEquals(
        'Extra properties are ignored. Use assertObjectEquals to compare them.',
        a1, a2);

    assertArrayEquals('An example where assertObjectEquals would fail in IE.',
        ['x'], 'x'.match(/x/g))
  }

  function testAssertObjectsEqualsDifferentArrays() {
    assertThrows('Should have thrown because args are different', function() {
      var a1 = ['className1'];
      var a2 = ['className2'];
      assertObjectEquals(a1, a2);
    });
  }

  function testAssertObjectsEqualsNegativeArrayIndexes() {
    var a1 = [0];
    var a2 = [0];
    a2[-1] = -1;
    // The following test fails unexpectedly. The bug is tracked at
    // http://code.google.com/p/closure-library/issues/detail?id=418
    // assertThrows('assertObjectEquals compares negative indexes', function() {
    //   assertObjectEquals(a1, a2);
    // });
  }

  function testAssertObjectsEqualsDifferentTypeSameToString() {
    assertThrows('Should have thrown because args are different', function() {
      var a1 = 'className1';
      var a2 = ['className1'];
      assertObjectEquals(a1, a2);
    });

    assertThrows('Should have thrown because args are different', function() {
      var a1 = ['className1'];
      var a2 = {'0': 'className1'};
      assertObjectEquals(a1, a2);
    });

    assertThrows('Should have thrown because args are different', function() {
      var a1 = ['className1'];
      var a2 = [['className1']];
      assertObjectEquals(a1, a2);
    });
  }

  function testAssertObjectsRoughlyEquals() {
    assertObjectRoughlyEquals({'a': 1}, {'a': 1.2}, 0.3);
    assertThrowsJsUnitException(
        function() { assertObjectRoughlyEquals({'a': 1}, {'a': 1.2}, 0.1); },
        'Expected <[object Object]> (Object) but was <[object Object]> ' +
        '(Object)\n   a: Expected <1> (Number) but was <1.2> (Number) which ' +
        'was more than 0.1 away');
  }

  function testFindDifferences_equal() {
    assertNull(goog.testing.asserts.findDifferences(true, true));
    assertNull(goog.testing.asserts.findDifferences(null, null));
    assertNull(goog.testing.asserts.findDifferences(undefined, undefined));
    assertNull(goog.testing.asserts.findDifferences(1, 1));
    assertNull(goog.testing.asserts.findDifferences([1, 'a'], [1, 'a']));
    assertNull(goog.testing.asserts.findDifferences(
        [[1, 2], [3, 4]], [[1, 2], [3, 4]]));
    assertNull(goog.testing.asserts.findDifferences(
        [{a: 1, b: 2}], [{b: 2, a: 1}]));
    assertNull(goog.testing.asserts.findDifferences(null, null));
    assertNull(goog.testing.asserts.findDifferences(undefined, undefined));
  }

  function testFindDifferences_unequal() {
    assertNotNull(goog.testing.asserts.findDifferences(true, false));
    assertNotNull(goog.testing.asserts.findDifferences(
        [{a: 1, b: 2}], [{a: 2, b: 1}]));
    assertNotNull(goog.testing.asserts.findDifferences(
        [{a: 1}], [{a: 1, b: [2]}]));
    assertNotNull(goog.testing.asserts.findDifferences(
        [{a: 1, b: [2]}], [{a: 1}]));
  }

  function testFindDifferences_objectsAndNull() {
    assertNotNull(goog.testing.asserts.findDifferences({a: 1}, null));
    assertNotNull(goog.testing.asserts.findDifferences(null, {a: 1}));
    assertNotNull(goog.testing.asserts.findDifferences(null, []));
    assertNotNull(goog.testing.asserts.findDifferences([], null));
    assertNotNull(goog.testing.asserts.findDifferences([], undefined));
  }

  function testFindDifferences_basicCycle() {
    var a = {};
    var b = {};
    a.self = a;
    b.self = b;
    assertNull(goog.testing.asserts.findDifferences(a, b));

    a.unique = 1;
    assertNotNull(goog.testing.asserts.findDifferences(a, b));
  }

  function testFindDifferences_crossedCycle() {
    var a = {};
    var b = {};
    a.self = b;
    b.self = a;
    assertNull(goog.testing.asserts.findDifferences(a, b));

    a.unique = 1;
    assertNotNull(goog.testing.asserts.findDifferences(a, b));
  }

  function testFindDifferences_asymmetricCycle() {
    var a = {};
    var b = {};
    var c = {};
    var d = {};
    var e = {};
    a.self = b;
    b.self = a;
    c.self = d;
    d.self = e;
    e.self = c;
    assertNotNull(goog.testing.asserts.findDifferences(a, c));
  }

  function testFindDifferences_basicCycleArray() {
    var a = [];
    var b = [];
    a[0] = a;
    b[0] = b;
    assertNull(goog.testing.asserts.findDifferences(a, b));

    a[1] = 1;
    assertNotNull(goog.testing.asserts.findDifferences(a, b));
  }

  function testFindDifferences_crossedCycleArray() {
    var a = [];
    var b = [];
    a[0] = b;
    b[0] = a;
    assertNull(goog.testing.asserts.findDifferences(a, b));

    a[1] = 1;
    assertNotNull(goog.testing.asserts.findDifferences(a, b));
  }

  function testFindDifferences_asymmetricCycleArray() {
    var a = [];
    var b = [];
    var c = [];
    var d = [];
    var e = [];
    a[0] = b;
    b[0] = a;
    c[0] = d;
    d[0] = e;
    e[0] = c;
    assertNotNull(goog.testing.asserts.findDifferences(a, c));
  }

  function testFindDifferences_multiCycles() {
    var a = {};
    a.cycle1 = a;
    a.test = {
      cycle2: a
    };

    var b = {};
    b.cycle1 = b;
    b.test = {
      cycle2: b
    };
    assertNull(goog.testing.asserts.findDifferences(a, b));
  }

  function testFindDifferences_binaryTree() {
    function createBinTree(depth, root) {
      if (depth == 0) {
        return {root: root};
      } else {
        var node = {};
        node.left = createBinTree(depth-1, root || node);
        node.right = createBinTree(depth-1, root || node);
        return node;
      }
    }

    // TODO(gboyer,user): This test does not terminate with the current
    // algorithm. Can be enabled when (if) the algorithm is improved.
    //assertNull(goog.testing.asserts.findDifferences(
    //    createBinTree(5, null), createBinTree(5, null)));
    assertNotNull(goog.testing.asserts.findDifferences(
        createBinTree(4, null), createBinTree(5, null)));
  }

  function testStringForWindowIE() {
    if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('8')) {
      // NOTE(user): This test sees of we are being affected by a JScript bug
      // in try/finally handling. This bug only affects the lowest try/finally
      // block in the stack. Calling this function via VBScript allows
      // us to run the test synchronously in an empty JS stack.
      window.execScript('stringForWindowIEHelper()', 'vbscript');
      assertEquals('<[object]> (Object)', window.stringForWindowIEResult);
    }
  }

  function testStringSamePrefix() {
    assertThrowsJsUnitException(
        function() { assertEquals('abcdefghi', 'abcdefghx'); },
        'Expected <abcdefghi> (String) but was <abcdefghx> (String)\n' +
        'Difference was at position 8. Expected [...ghi] vs. actual [...ghx]');
  }

  function testStringSameSuffix() {
    assertThrowsJsUnitException(
        function() { assertEquals('xbcdefghi', 'abcdefghi'); },
        'Expected <xbcdefghi> (String) but was <abcdefghi> (String)\n' +
        'Difference was at position 0. Expected [xbc...] vs. actual [abc...]');
  }

  function testStringDissimilarShort() {
    assertThrowsJsUnitException(
        function() { assertEquals('x', 'y'); },
        'Expected <x> (String) but was <y> (String)');
  }

  function testStringDissimilarLong() {
    assertThrowsJsUnitException(
        function() { assertEquals('xxxxxxxxxx', 'yyyyyyyyyy'); },
        'Expected <xxxxxxxxxx> (String) but was <yyyyyyyyyy> (String)');
  }

  function testAssertElementsEquals() {
    assertElementsEquals([1, 2], [1, 2]);
    assertElementsEquals([1, 2], {0: 1, 1: 2, length: 2});
    assertElementsEquals('Good assertion', [1, 2], [1, 2]);
    assertThrowsJsUnitException(
        function() {
          assertElementsEquals('Message', [1, 2], [1]);
        },
        'length mismatch: Message\n' +
        'Expected <2> (Number) but was <1> (Number)');
  }

  function testStackTrace() {
    try {
      assertTrue(false);
    } catch (e) {
      if (Error.captureStackTrace) {
        assertNotUndefined(e.stack);
      }
      if (e.stack) {
        assertContains('testStackTrace', e.stack);
      }
    }
  }

  function stringForWindowIEHelper() {
    window.stringForWindowIEResult = _displayStringForValue(window);
  }

  function testDisplayStringForValue() {
    assertEquals('<hello> (String)', _displayStringForValue('hello'));
    assertEquals('<1> (Number)', _displayStringForValue(1));
    assertEquals('<null>', _displayStringForValue(null));
    assertEquals('<undefined>', _displayStringForValue(undefined));
    assertEquals('<hello,,,,1> (Array)', _displayStringForValue(
        ['hello', /* array hole */, undefined, null, 1]));
  }

  function testDisplayStringForValue_exception() {
    assertEquals('<toString failed: foo message> (Object)',
        _displayStringForValue({toString: function() {
          throw Error('foo message');
        }}));
  }

  function testDisplayStringForValue_cycle() {
    var cycle = ['cycle'];
    cycle.push(cycle);
    assertTrue(
        'Computing string should terminate and result in a reasonable length',
        _displayStringForValue(cycle).length < 1000);
  }

</script>
</body>
</html>
